﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using JESAI.Core.Extensions;

namespace JESAI.Core.Extensions
{ 
    /// <summary>
    /// <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/> 扩展类
    /// </summary>
    public static class DictionaryExtensions
    {
        /// <summary>
        /// 从<paramref name="dictionary"/>中获取值，如果指定的 <paramref name="key"/> 不存在的话，就将<paramref name="value"/>添加进去
        /// </summary>
        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
        {
            if (dictionary.TryGetValue(key, out var result)) { return result; }

            dictionary[key] = value;
            return value;
        }

        /// <summary>
        /// 从<paramref name="dictionary"/>中获取值，如果指定的 <paramref name="key"/> 不存在的话，就调用<paramref name="valueCreator"/>生成值并添加进去
        /// </summary>
        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TValue> valueCreator)
        {
            if (dictionary.TryGetValue(key, out var result)) { return result; }

            var value = valueCreator();
            dictionary[key] = value;
            result = value;
            return result;
        }

        /// <summary>
        /// 从<paramref name="dictionary"/>中获取值，如果指定的 <paramref name="key"/> 不存在的话，返回指定的 <paramref name="defaultValue"/>
        /// </summary>
        public static TValue GetOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary,
            TKey key, TValue defaultValue = default)
                => dictionary.TryGetValue(key, out var value) ? value : defaultValue;

        /// <summary>
        /// 将给定的字典 <paramref name="pairsToAdd"/> 中的数据添加到当前字典中
        /// </summary>
        public static void Add<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IDictionary<TKey, TValue> pairsToAdd)
        {
            foreach (var pair in pairsToAdd)
            {
                dictionary.Add(pair.Key, pair.Value);
            }
        }

        /// <summary>
        /// 比较当前字典和给定的字典 <paramref name="right"/> 是否相等，使用<paramref name="valueComparer"/>比较器对集合中的元素进行比较
        /// </summary>
        public static bool Equals<TKey, TValue>(this IDictionary<TKey, TValue> left,
            IDictionary<TKey, TValue> right, IEqualityComparer<TValue> valueComparer = default)
                => Equals((IReadOnlyDictionary<TKey, TValue>)left, (IReadOnlyDictionary<TKey, TValue>)right, valueComparer);

        /// <summary>
        /// 将<see cref="NameValueCollection"/> 转换成字典
        /// </summary>
        public static Dictionary<string, string> ToDictionary(this NameValueCollection namedValueCollection)
            => namedValueCollection.AllKeys.ToDictionary(key => key, key => namedValueCollection[key]);

        /// <summary>
        /// 将当前字典转换为线程安全的字典
        /// </summary>
        public static ConcurrentDictionary<TKey, TValue> ToConcurrentDictionary<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary) =>
            new ConcurrentDictionary<TKey, TValue>(dictionary);

        /// <summary>
        /// 将当前只读字典转换为线程安全的字典
        /// </summary>
        public static ConcurrentDictionary<TKey, TValue> ToConcurrentDictionary<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary,
            IEqualityComparer<TKey> comparer)
                => new ConcurrentDictionary<TKey, TValue>(dictionary, comparer);

        /// <summary>
        /// 使用指定的比较器将当前的字典与指定的字典<paramref name="right"/>进行比较
        /// </summary>
        public static bool Equals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> left,
            IReadOnlyDictionary<TKey, TValue> right, IEqualityComparer<TValue> valueComparer = default)
        {
            if (left == right) { return true; }
            if (left is null || right == null) { return false; }
            if (left.Count != right.Count) { return false; }
            if (left.Count == 0) { return true; }

            var comparer = valueComparer ?? EqualityComparer<TValue>.Default;

            if (left is Dictionary<TKey, TValue> leftConcrete)
            {
                foreach (var pair in leftConcrete)
                {
                    if (!KeyValueExists(pair.Key, pair.Value, right, comparer)) { return false; }
                }
            }
            else if (right is Dictionary<TKey, TValue> rightConcrete)
            {
                foreach (var pair in rightConcrete)
                {
                    if (!KeyValueExists(pair.Key, pair.Value, left, comparer)) { return false; }
                }
            }
            else
            {
                foreach (var pair in left)
                {
                    if (!KeyValueExists(pair.Key, pair.Value, right, comparer)) { return false; }
                }
            }
            return true;
        }

        private static bool KeyValueExists<TKey, TValue>(
            TKey key, TValue value, IReadOnlyDictionary<TKey, TValue> dictionary, IEqualityComparer<TValue> comparer)
                => dictionary.TryGetValue(key, out var rightVal) && comparer.Equals(value, rightVal);

        /// <summary>
        /// 将字典转换成实体类,如果字典为null则返回default(T)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dic">字典</param>
        /// <returns></returns>
        public static T ToModel<T>(this Dictionary<string, object> dic) where T : class, new()
        {
            if (dic == null) return default;
            var colNames = dic.Keys.ToList();
            var map = GetPropMapper(colNames, typeof(T), true, true);
            var model = new T();
            foreach (var item in map)
            {
                var val = item.Value;
                if (val.CanWrite) val.SetValue(model, dic[item.Key].To(val.PropertyType));
            }
            return model;
        }

        /// <summary>
        /// 将字典集合转换成实体类,如果字典为null则返回default(T)<para></para>
        /// 注意：字典集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dics">字典集合</param>
        /// <returns></returns>
        public static List<T> ToModels<T>(this IList<Dictionary<string, object>> dics) where T : class, new()
        {
            if (dics == null || dics.Count == 0) return new List<T>();
            var _dic = dics[0];
            var colNames = _dic.Keys.ToList();
            var map = GetPropMapper(colNames, typeof(T), true, true);
            var list = new List<T>();
            foreach (var dic in dics)
            {
                var model = new T();
                foreach (var item in map)
                {
                    var val = item.Value;
                    if (val.CanWrite) val.SetValue(model, dic[item.Key].To(val.PropertyType));
                }
                list.Add(model);
            }
            return list;
        }

        /// <summary>
        /// 列名到模型属性的映射
        /// </summary>
        /// <param name="colNames">列名</param>
        /// <param name="type">模型类型</param>
        /// <param name="isIgnoreCase">是否忽略大小写</param>
        /// <param name="isIgnoreUnderline">是否忽略下划线</param>
        /// <returns></returns>
        public static Dictionary<string, PropertyInfo> GetPropMapper(List<string> colNames, Type type, bool isIgnoreCase, bool isIgnoreUnderline)
        {
            var dic = new Dictionary<string, PropertyInfo>();
            if (type == null || dic == null) return dic;
            var props = type.GetProperties().ToList();
            //第一遍先忽略大小去匹配名称
            for (int i = colNames.Count - 1; i >= 0; i--)
            {
                var colname = colNames[i];
                for (int j = props.Count - 1; j >= 0; j--)
                {
                    if (string.Equals(colname, props[j].Name, isIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
                    {
                        dic.Add(colname, props[j]);
                        props.RemoveAt(j);
                        colNames.RemoveAt(i);
                        break;
                    }
                }
            }
            if (isIgnoreUnderline)
            {
                //第二遍忽略掉"_"符号
                for (int i = colNames.Count - 1; i >= 0; i--)
                {
                    var colname = colNames[i];
                    for (int j = props.Count - 1; j >= 0; j--)
                    {
                        var colname2 = colname.Replace("_", "");
                        var propname2 = props[j].Name.Replace("_", "");
                        if (string.Equals(colname2, propname2, StringComparison.OrdinalIgnoreCase))
                        {
                            dic.Add(colname, props[j]);
                            props.RemoveAt(j);
                            colNames.RemoveAt(i);
                            break;
                        }
                    }
                }
            }
            return dic;
        }

        /// <summary>
        /// Fluent风格: 从字典中移除项,并返回自身
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果字典为null,抛出异常</item>
        /// <item>如果key不存在,则跳过</item>
        /// </list>
        /// </remarks>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> RemoveFluent<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key)
        {
            if (dic.ContainsKey(key)) dic.Remove(key);
            return dic;
        }

        /// <summary>
        /// Fluent风格: 从字典中移除项,并返回自身
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果字典为null,抛出异常</item>
        /// <item>如果key不存在,则跳过</item>
        /// </list>
        /// </remarks>
        /// <returns></returns>
        public static Dictionary<TKey, TValue> RemoveFluent<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key)
        {
            if (dic.ContainsKey(key)) dic.Remove(key);
            return dic;
        }

        /// <summary>
        /// Fluent风格: 向字典中添加或更新项,并返回自身
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果字典为null,抛出异常</item>
        /// </list>
        /// </remarks>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> SetFluent<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key, TValue value)
        {
            dic[key] = value;
            return dic;
        }

        /// <summary>
        /// Fluent风格: 向字典中添加或更新项,并返回自身
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果字典为null,抛出异常</item>
        /// </list>
        /// </remarks>
        /// <returns></returns>
        public static Dictionary<TKey, TValue> SetFluent<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue value)
        {
            dic[key] = value;
            return dic;
        }
    }
}